package util;

import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.RenderingHints.Key;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.HashMap;
import java.util.concurrent.ExecutionException;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.border.Border;
import sun.awt.image.BufferedImageGraphicsConfig;

public class ImageFileChooserImagePanel extends JPanel {
	private static final long serialVersionUID = 1L;
	private Image image = new ImageIcon("./src/ui/images/thumb_empty.jpg").getImage();
	private RenderingHints hints;
	protected BufferedImage imagethumb;
	protected int Xwidth;
	protected int Xheight;
	

	public ImageFileChooserImagePanel() {
		Border empty = BorderFactory.createRaisedBevelBorder();
//		Border empty = BorderFactory.cr;
////		Border border = new CompoundBorder(BorderFactory.createEtchedBorder(), empty);
		setBorder(empty);
		HashMap<Key, Object> map = new HashMap<Key, Object>();
		map.put(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);

		map.put(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);

		map.put(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		hints = new RenderingHints(map);
	}

	public void setImage(Image image) {
		this.image = image;//.getWidth(null)<=0?new ImageIcon("./src/ui/images/thumb_empty.jpg").getImage():image;
		rebuildThumb();
	}
	
	private void rebuildThumb() {
		SwingWorker<BufferedImage, Void> workerinit = new SwingWorker<BufferedImage, Void>() {
			@Override
			protected BufferedImage doInBackground() {
				BufferedImage resizedImage = null;
				try {
					BufferedImage image = toBufferedImage(ImageFileChooserImagePanel.this.image, false);
					int type = image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image
							.getType();
					ImageFileChooserImagePanel.this.Xheight = getHeight()>0?getHeight():158;
					ImageFileChooserImagePanel.this.Xwidth = getWidth()>0?getWidth():204;					
					
					resizedImage = new BufferedImage(Xwidth,Xheight, type);
					Graphics2D g = resizedImage.createGraphics();
					g.setComposite(AlphaComposite.Src);
					g.addRenderingHints(hints);
					g.setColor(ImageFileChooserImagePanel.this.getBackground());
					g.fillRect(0, 0, Xwidth, Xheight);
					if (image != null) {
						Image scaled = getScaled();
						g.drawImage(scaled,	
								(Xwidth / 2 - scaled.getWidth(ImageFileChooserImagePanel.this) / 2),								
								(Xheight	/ 2 - scaled.getHeight(ImageFileChooserImagePanel.this) / 2),
								ImageFileChooserImagePanel.this);
					}
					g.dispose();
				} catch (Exception e) {					
					System.err.println("Abgefangen in SwingWorker workerinit in ImageFileChooser - ");
					e.printStackTrace();
				}
				return resizedImage;
			}

			@Override
			protected void done() {
				try {
					ImageFileChooserImagePanel.this.imagethumb=this.get();
				} catch (InterruptedException e) {					
					e.printStackTrace();
				} catch (ExecutionException e) {					
					e.printStackTrace();
				}
				ImageFileChooserImagePanel.this.repaint();
				ImageFileChooserImagePanel.this.image=null;
				
			}
			
			@Override
			public String toString() {
				return "ifc.worker";
			}
		};
		
		workerinit.execute();	
	}

	@Override
	public void paintComponent(Graphics g) {
		if (g != null) {
			Graphics scratch = g.create();
			scratch.setColor(getBackground());
			scratch.fillRect(0, 0, getWidth(), getHeight());
			if (imagethumb != null) {
				Image scaled = imagethumb;
				scratch.drawImage(scaled,	
						(getWidth() / 2 - scaled.getWidth(ImageFileChooserImagePanel.this) / 2),								
						(getHeight()	/ 2 - scaled.getHeight(ImageFileChooserImagePanel.this) / 2),
						ImageFileChooserImagePanel.this);
			}
		}
	}

	

	private Image getScaled() {
//		int iw = image.getWidth(this);
//		int ih = image.getHeight(this);
//		int pw = getWidth()-20;
//		int ph = getHeight()-20;
//		double scale;
//		if (1.0 * pw / iw < 1.0 * ph / ih) {
//			scale = 1.0 * pw / iw;
//		} else {
//			scale = 1.0 * ph / ih;
//		}
//		int scaledw = (int) (iw * scale);
//		int scaledh = (int) (ih * scale);
//		if (scaledCache != null) {
//			if (scaledCache.getWidth(this) == scaledw
//					&& scaledCache.getHeight(this) == scaledh) {
//				return scaledCache;
//			}
//		}
//		scaledCache = image.getScaledInstance(scaledw, scaledh,
//				Image.SCALE_DEFAULT);
//		return scaledCache;
		return image==null?null:resizeHQ(toBufferedImage(image,false),Xwidth-20,Xheight-20);
	}
	
	
	public BufferedImage resizeHQ(BufferedImage image, int width, int height) {
		image = createCompatibleImage(image);
		image = resize(image, width+((image.getWidth()-width)/2), height+((image.getHeight()-height)/2));
		image = filter(image,image);
		image = resize(image, width, height);
		return image;
	}
	
	public BufferedImage blurImage(BufferedImage image) {
		float ninth = 1.0f / 9.0f;
		float[] blurKernel = { ninth, ninth, ninth, ninth, ninth, ninth, ninth,
				ninth, ninth };
		BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel),
				ConvolveOp.EDGE_NO_OP, hints);
		return op.filter(image, null);
	}

	private BufferedImage createCompatibleImage(BufferedImage image) {
		GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);
		int w = image.getWidth();
		int h = image.getHeight();
		BufferedImage result = gc.createCompatibleImage(w, h,
				Transparency.TRANSLUCENT);
		Graphics2D g2 = result.createGraphics();
		g2.addRenderingHints(hints);
		g2.drawRenderedImage(image, null);
		g2.dispose();
		return result;
	}
	
	private BufferedImage toBufferedImage(Image image, boolean hasAlpha) {
		if (image instanceof BufferedImage) {
			return (BufferedImage) image;
		}
		image = new ImageIcon(image).getImage();
		BufferedImage bimage = null;
		GraphicsEnvironment ge = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		try {
			int transparency = hasAlpha ? Transparency.BITMASK
					: Transparency.OPAQUE;
			GraphicsDevice gs = ge.getDefaultScreenDevice();
			GraphicsConfiguration gc = gs.getDefaultConfiguration();
			bimage = gc.createCompatibleImage(image.getWidth(null),
					image.getHeight(null), transparency);
		} catch (HeadlessException e) {
		}

		if (bimage == null) {

			int type = BufferedImage.TYPE_INT_RGB;
			if (hasAlpha) {
				type = BufferedImage.TYPE_INT_ARGB;
			}
			bimage = new BufferedImage(image.getWidth(null),
					image.getHeight(null), type);
		}

		Graphics g = bimage.getGraphics();

		// Paint the image onto the buffered image
		g.drawImage(image, 0, 0, null);
		g.dispose();

		return bimage;
	}

	private BufferedImage resize(BufferedImage image, int width, int height) {
		int type = image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image
				.getType();
		BufferedImage resizedImage = new BufferedImage(width, height, type);
		Graphics2D g = resizedImage.createGraphics();
		g.setComposite(AlphaComposite.Src);
		g.addRenderingHints(hints);

		g.drawImage(image, 0, 0, width, height, null);
		g.dispose();
		return resizedImage;
	}
	
	
	//***************************
	
	private int hRadius =1;
	private int vRadius=1;
	private int iterations = 1;
	

	
    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
        int width = src.getWidth();
        int height = src.getHeight();

        if ( dst == null )
            dst = createCompatibleImage( src );

        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );

        for (int i = 0; i < iterations; i++ ) {
            blur( inPixels, outPixels, width, height, hRadius );
            blur( outPixels, inPixels, height, width, vRadius );
        }

        setRGB( dst, 0, 0, width, height, inPixels );
        return dst;
    }
    
    public int[] getRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
		int type = image.getType();
		if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
			return (int [])image.getRaster().getDataElements( x, y, width, height, pixels );
		return image.getRGB( x, y, width, height, pixels, 0, width );
    }

	/**
	 * A convenience method for setting ARGB pixels in an image. This tries to avoid the performance
	 * penalty of BufferedImage.setRGB unmanaging the image.
	 */
	public void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {
		int type = image.getType();
		if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )
			image.getRaster().setDataElements( x, y, width, height, pixels );
		else
			image.setRGB( x, y, width, height, pixels, 0, width );
    }
	
	public static int clamp(int x, int a, int b) {
		return (x < a) ? a : (x > b) ? b : x;
	}

    public static void blur( int[] in, int[] out, int width, int height, int radius ) {
        int widthMinus1 = width-1;
        int tableSize = 2*radius+1;
        int divide[] = new int[256*tableSize];

        for ( int i = 0; i < 256*tableSize; i++ )
            divide[i] = i/tableSize;

        int inIndex = 0;
        
        for ( int y = 0; y < height; y++ ) {
            int outIndex = y;
            int ta = 0, tr = 0, tg = 0, tb = 0;

            for ( int i = -radius; i <= radius; i++ ) {
                int rgb = in[inIndex + clamp(i, 0, width-1)];
                ta += (rgb >> 24) & 0xff;
                tr += (rgb >> 16) & 0xff;
                tg += (rgb >> 8) & 0xff;
                tb += rgb & 0xff;
            }

            for ( int x = 0; x < width; x++ ) {
                out[ outIndex ] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb];

                int i1 = x+radius+1;
                if ( i1 > widthMinus1 )
                    i1 = widthMinus1;
                int i2 = x-radius;
                if ( i2 < 0 )
                    i2 = 0;
                int rgb1 = in[inIndex+i1];
                int rgb2 = in[inIndex+i2];
                
                ta += ((rgb1 >> 24) & 0xff)-((rgb2 >> 24) & 0xff);
                tr += ((rgb1 & 0xff0000)-(rgb2 & 0xff0000)) >> 16;
                tg += ((rgb1 & 0xff00)-(rgb2 & 0xff00)) >> 8;
                tb += (rgb1 & 0xff)-(rgb2 & 0xff);
                outIndex += height;
            }
            inIndex += width;
        }
    }
        
	public void setHRadius(int hRadius) {
		this.hRadius = hRadius;
	}
	
	public int getHRadius() {
		return hRadius;
	}
	
	public void setVRadius(int vRadius) {
		this.vRadius = vRadius;
	}
	
	public int getVRadius() {
		return vRadius;
	}
	
	public void setRadius(int radius) {
		this.hRadius = this.vRadius = radius;
	}
	
	public int getRadius() {
		return hRadius;
	}
	
	public void setIterations(int iterations) {
		this.iterations = iterations;
	}
	
	public int getIterations() {
		return iterations;
	}
	
	public String toString() {
		return "Blur/Box Blur...";
	}



}